package com.cnd.scm.jdbcprocessor.dbentity;


import com.cnd.scm.common.cache.CacheManagerImpl;
import com.cnd.scm.common.cache.ICacheManager;

import java.sql.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 获取和缓存向数据库中请求的SQL语句或参数 工具类
 * 目的：减少向数据库的频繁连接，提升访问操作性能
 * @author 李启联
 * @version 1.1
 *
 */
public final class DBParameterCache {

	// 定义一个私有构造方法
	private DBParameterCache(){
		super();
	}

    //定义一个静态私有变量(不初始化，不使用final关键字，使用volatile保证了多线程访问时instance变量的可见性，避免了instance初始化时其他变量属性还没赋值完时，被另外线程调用)
    //加volatile 禁止指令重排序
    private static volatile DBParameterCache instance;

    //定义一个共有的静态方法，返回该类型实例
    public static DBParameterCache getInstance() { 
        // 对象实例化时与否判断（不使用同步代码块，instance不等于null时，直接返回对象，提高运行效率）
        if (instance == null) {
            //同步代码块（对象未初始化时，使用同步代码块，保证多线程访问时对象在第一次创建后，不再重复被创建）
            synchronized (DBParameterCache.class) {
                //未初始化，则初始instance变量
                if (instance == null) {
                    instance = new DBParameterCache();   
                }   
            }   
        }   
        return instance;   
    } 
    
	//存储SQL语句或参数的 HashMap
	private static final ConcurrentHashMap cacheMap = new ConcurrentHashMap();

    //使用本地简单缓存实现
    private static  final ICacheManager cacheManager = new CacheManagerImpl();

    private static final long expireTime = 60L ; //设置过期时间为60分钟
	
	/**
	 * 从缓存在哈希MAP中得到对应名称的存储过程的参数设置
	 * @param conn 数据库链接对象
	 * @param spName 存储过程名称
	 * @return 数据库参数集合
	 * @throws SQLException
	 */
	public static DBParameterCollection GetCachedParameterSet(Connection conn, String spName) throws SQLException{
		DBParameterCollection dbParmCollection = null;
		
//		if((DBParameterCollection)cacheMap.get(conn.getMetaData().getURL()+":"+spName) != null){
//			dbParmCollection = (DBParameterCollection)cacheMap.get(conn.getMetaData().getURL()+":"+spName);
//		}
        if(cacheManager.getCacheDataByKey(conn.getMetaData().getURL()+":"+spName) != null && !cacheManager.isTimeOut(conn.getMetaData().getURL()+":"+spName)){
            dbParmCollection = (DBParameterCollection)cacheManager.getCacheDataByKey(conn.getMetaData().getURL()+":"+spName);
        }
		
		return dbParmCollection;
	}
	
	
	/**
	 * 得到数据库中存储过程中设置的参数集合
	 * @param conn 数据库链接对象
	 * @param spName 存储过程名称
	 * @return 数据库参数集合
	 * @throws SQLException
	 */
	public  static DBParameterCollection GetSpParameterSet(Connection conn, String spName) throws SQLException{
		DBParameterCollection dbParmCollection = discoverSpParamCollection(conn,spName,false);
		return dbParmCollection;
	}
	
	/**
	 * 往HashMap中存入指定名称存储过程的参数设置
	 * @param conn 数据库连接对象
	 * @param spName 存储过程名称
	 * @param dbParamCollection 数据库参数设置集合
	 * @throws Exception
	 */
	public synchronized static void CacheParameterSet(Connection conn, String spName, DBParameterCollection dbParamCollection) throws Exception{
		if(conn == null) 
			throw new Exception("数据连接不能为空...");
		if(spName ==null || spName.length() == 0)
			throw new Exception("数据库执行语句或存储过程名称不能为空...");
		
		String hashKey = conn.getMetaData().getURL() + ":" + spName;
			
		//cacheMap.put(hashKey,dbParamCollection);
        cacheManager.putCache(hashKey,dbParamCollection,expireTime);
	}
	

	/**
	 * 得到数据库中存储过程中设置的参数集合
	 * @param conn 数据库链接对象
	 * @param spName 存储过程名称
	 * @param includeReturnValueParameter 是否包含系统默认的RETURN_VALUE参数
	 * @return 数据库参数集合
	 */
	public static DBParameterCollection discoverSpParamCollection(Connection conn, String spName,
			boolean includeReturnValueParameter) {
		DBParameterCollection dbParmCollection = new DBParameterCollection();
		try {
			ResultSet rs = null;
			ResultSetMetaData md = null;
			DatabaseMetaData dbMd = conn.getMetaData();
			rs = dbMd.getProcedureColumns(null, null, spName, null);
			md = rs.getMetaData();
			String colName = "";
			int dbType = Types.NVARCHAR;
			int colType = DatabaseMetaData.procedureColumnIn;
			DBParameterDirection direction = DBParameterDirection.Input;
			while (rs.next()) {
				colName = rs.getObject("COLUMN_NAME").toString();//.toLowerCase();
				if (colName.startsWith("@") || colName.startsWith("?") || colName.startsWith(":"))
					colName = colName.substring(1);
				if (colName.equalsIgnoreCase("RETURN_VALUE") && !includeReturnValueParameter)
					continue;
				dbType = Integer.parseInt( rs.getObject("DATA_TYPE").toString());
				colType = Integer.parseInt(rs.getObject("COLUMN_TYPE").toString());
				//System.out.println(colType);
				//System.out.println(DatabaseMetaData.procedureColumnIn +" ; "+DatabaseMetaData.procedureColumnOut+" ; "+DatabaseMetaData.procedureColumnInOut);
				switch (colType) {
				case (int) DatabaseMetaData.procedureColumnIn:
					direction = DBParameterDirection.Input;
					break;
				case (int) DatabaseMetaData.procedureColumnOut:
					direction = DBParameterDirection.Output;
					break;
				case (int) DatabaseMetaData.procedureColumnInOut:
					direction = DBParameterDirection.InputOutput;
					break;
				default:
					direction = DBParameterDirection.Input;
					break;
				}

				DBParameter param = new DBParameter();
				param.setParameterName(colName);
				param.setDbType(dbType);
				param.setParameterDirection(direction);
				dbParmCollection.add(param);
			}
		} catch (Exception e) {
			return null;
		}
		return dbParmCollection;
	}
	
	/**
	 * 得到数据库中存储过程中设置的参数DBParameter集合
	 * @param conn 数据库链接对象
	 * @param spName 存储过程名称
	 * @param includeReturnValueParameter 是否包含系统默认的RETURN_VALUE参数
	 * @return 数据库参数DBParameter集合
	 * @throws SQLException
	 */
	public static DBParameter[] discoverSpParameters(Connection conn,String spName, boolean includeReturnValueParameter) throws SQLException{
		return (DBParameter[])discoverSpParamCollection(conn, spName, includeReturnValueParameter).toArray();
	}
	
	
	
}
